<?php
class TdApplication {
  var $sysroot = "";
  var $webroot = "";
  var $request = "";
  var $cookies = array();
  var $page_path = "";
  var $page_name = "";
  var $page_component = "";
  var $page_args = array();
  var $lang = "";
  var $errormessage = "";

  var $page_class = "";
  var $controller = "";
  var $arguments = array();
  var $method_args = array();
  var $config = array();
  
  private $eot = false;

  private function parseHTTPRequest(){
    $this->webroot = "http://".$_SERVER['SERVER_NAME'].":".$_SERVER['SERVER_PORT'].substr($_SERVER['PHP_SELF'],0,strlen($_SERVER['PHP_SELF']) - strlen("/index.php"));
    $this->request = $_SERVER['QUERY_STRING'];

    $GLOBALS["webroot"] = $this->webroot;
  }

  private function parseRequestURL() {
  	$pmethod = '';
  	$pcomponent = '';
  	$cmethod = '';
  	$tdargs = '';
  	
    // URL rewriting schema:
    // /path/classname$component+method.htm/arg0/val0/arg1/val1
    // /path/classname+method.htm/arg0/val0/arg1/val1
    // Method arguments can be passed as $# name. ie:
    // /path/classnam.htm/$0/val2/$1/val2 calls __construct(val1, val2)
  	// /path/classnam+method.htm/$0/val2/$1/val2 calls classname::method(val1, val2)
  	// /path/classnam$component+method.htm/$0/val2/$1/val2 calls classname::component::method(val1, val2)

    //echo $_REQUEST['tdroute']." | ".$_REQUEST['tdargs']."<br>";

    if (!isset($_REQUEST['tdroute']) || $_REQUEST['tdroute'] == "") {
      $_REQUEST['tdroute'] = $this->config['default_route'];    
    }

    $pos = strrpos($_REQUEST['tdroute'],"/");
    if ($pos) {
      $ppath = substr($_REQUEST['tdroute'], 0, $pos);
      $proute = substr($_REQUEST['tdroute'], $pos + 1);
    }
    else {
      $ppath = "";
      $proute = $_REQUEST['tdroute'];
    }    
    $this->page_path = $ppath;
    
    $pos = strpos($proute, "$");
    if ($pos) { # got component
	    $pname = substr($proute, 0, $pos);
        $ppos = $pos + 1;
        $pos = strpos($proute, " ", $ppos);
        if ($pos) { # got component method
          $pcomponent = substr($proute, $ppos, $pos - $ppos);
          $cmethod = substr($proute, $pos + 1);          	        
        }
        else {
	      $pcomponent = substr($proute, $ppos);
        }
    }
    else {
        $pos = strpos($proute, " ");
        if ($pos) { # got class method
	      $pname = substr($proute, 0, $pos);
	      $pmethod = substr($proute, $pos + 1);
       }
       else {
	     $pname = $proute;
       }	    
    }
    
    $this->page_name = $pname;
    $this->page_method = $pmethod;
    $this->page_component = $pcomponent;
    $this->component_method = $cmethod;
    $this->page_class = str_replace("-", "", $this->page_name);
    $this->page_controller = $this->sysroot."/app/".$this->page_path.$this->page_class.".php";

    /*
    echo "page name = ".$this->page_name."<br>";
    echo "page method = ".$this->page_method."<br>";
    echo "page component = ".$this->page_component."<br>";
    echo "component method = ".$this->component_method."<br>";
    echo "page class = ".$this->page_class."<br>";
    echo "page controller = ".$this->page_controller;
    */
    
    //
    // Let collect the rest as name/value pairs arguments
    // 
    if (isset($_REQUEST['tdargs'])) {
	    $tokens = explode('/', $_REQUEST['tdargs']);
	    $varname = "";
	    $odd = TRUE;
	
	    foreach($tokens as $val) {
	      if ($odd) {
	        $varname = $val;
	      }
	      else {
	        if (strpos($varname, '$') === 0) {
	        	$ind = (int)substr($varname, 1);
	        	$this->method_args[$ind] = $val;
	        }
	        else {
	        	$this->arguments[$varname] = $val;
	        }
	      }
	      $odd = !$odd;
	    }
    }

    //
    // Collect submitted and global arguments
    //
    $this->arguments = array_merge($this->arguments, $_REQUEST);
    $this->arguments['sysroot'] = $GLOBALS['sysroot'];
    $this->arguments['webroot'] = $GLOBALS['webroot'];
    //var_dump($_SESSION);
    //var_dump($this->arguments);
  }

  private function flush() {
    ob_start("ob_gzhandler");
    print($this);
    @ob_end_flush(); // warns if ob is empty: prepending @
  }

  function createcookie($name, $value, $expire = 0) {
    $this->cookies[$name] = array("value" => $value, "expire" => $expire);
  }
  
  function readcookie($name) {
  	return (isset($_COOKIE[$name])) ? $_COOKIE[$name] : "";
  }
  
  function deletecookie($name) {
  	$this->cookies[$name] = array("value" => "", "expire" => time()-1);
  }
  
  function switchLocale() {
  	global $teardrop;
  	
  	$locale = $teardrop->config['default_locale'];
  	
  	if (isSet($teardrop->arguments["locale"])) $locale = $teardrop->arguments["locale"];
  	putenv("LC_ALL=$locale");
  	setlocale(LC_ALL, $locale);
  	bindtextdomain("default", $teardrop->config['locale_path']);
  	textdomain("default");
  }
  

  function tdexception_handler($ex) {
  	print("<b>Exception:</b> ".$ex->getMessage());
  }
  
  function __construct($sysroot, $config="") {

  	$this->sysroot = $sysroot;
  	
    if ($config == "") $config = $this->sysroot."/conf/tdconfig.php";

    require_once($config);

    $this->config = $tdconfig;
    
  }
  
  function run() {
  	@set_exception_handler(array($this, 'tdexception_handler'));

    $this->parseHTTPRequest();

    $this->parseRequestURL();

    // Select current page locale
    $this->switchLocale();
    
    //it seems that session autocommit is disabled during next flush phase.
    session_commit();

    $this->flush(); 
  }

  function route($page, $args = "") {
    ob_end_clean();
    $tokens = explode('.', $page);
    $_REQUEST['tdroute'] = $tokens[0];
    $_REQUEST['tdsub'] = $tokens[1];
    if ($args != "") {
      $_REQUEST['tdargs'] = $args;
    }
    $this->parseRequestURL();
    $this->flush();
    // avoid continue rendering the previus object
    $this->eot = true;
  }

  function redirect($url) {
  	// cookies must precede location header if any
  	foreach ($this->cookies as $name => $values) {
  		setcookie($name, $values['value'], $values['expire']);
  		unset($this->cookies[$name]);
  	}
    header("Location: ".$url);
    // must be completed avoiding the rendering of any content except this header
  }
  
  function __toString() {
    if ($this->page_class != "" && is_file($this->page_controller)) {
      require_once($this->page_controller);
      //*** call_user_func doesn't work with getInstance in PHP < 5.3
      //$pageobj = call_user_func_array(array($this->page_class, 'getInstance'), $this->method_args);
      $rc = new ReflectionClass($this->page_class);
      $pageobj = $rc->newInstanceArgs($this->method_args);
      //***
      $pageobj->parent = $this;
      $pageobj->doCheck();
      $pageobj->doInit();
      $gotsubmit = preg_grep('/^tdform/', array_keys($this->arguments));
      if (! empty($gotsubmit)) {
        $pageobj->doValidate();
      }
      // exiting from page events we should end the transmition because of different routes
      if ($this->eot) {
      	return "";
      }
      if ($this->page_component != "" && $pageobj->components[$this->page_component]) {
      	// Request is a page component
        if ($this->component_method != "") {
          // Render the result of a page component method
        	$rc = new ReflectionClass($pageobj->components[$this->page_component]);
        	$pageobj = $rc->newInstanceArgs($this->method_args);
	        return call_user_func_array(array($pageobj->components[$this->page_component], $this->component_method), $this->method_args);
        }
        else {
        	// Render a page component
          return $pageobj->components[$this->page_component]->__toString();
        }
      }
      else {
      	// Request is a page
      	// should be placed in tdpage.php in the future...
      	foreach ($this->cookies as $name => $values) {
      		setcookie($name, $values['value'], $values['expire']);
      		unset($this->cookies[$name]);
      	}
        if ($this->page_method != "") {
        	// Render a page method
          return call_user_func_array(array($pageobj, $this->page_method), $this->method_args);
        }
        else {
        	// Render a page
          return $pageobj->__toString();
        }
      }
    }
    else {
    	// Request is not found
      if (!isset($this->config['404_route']) || $this->config['404_route'] == "") {
      	// Render a NOT FOUND error
        header('HTTP/1.0 404 Not Found');
        return "";
      }
      else {
      	// Render the configured NOT FOUND route
        $this->route($this->config['404_route']);
        return "";
      }
    }
  }
}
?>
